package io.renren.iots.socket.client;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.CancelledKeyException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.renren.modules.iotp.entity.IOTPBean;
import io.renren.modules.iotp.utils.IotpBodyUtils;

/**
 * @ClassName: SingleSocketWriteThread
 * @Description: 单例模式SingleSocketWriteThread类
 * @author 周西栋
 * @date 2018年5月8日
 *
 */
public class SingleSocketWriteThread implements Runnable {
	
	/**
	 * 日志
	 */
	private final static Logger LOGGER = LoggerFactory.getLogger(SingleSocketWriteThread.class);
	
	/*发送数据缓冲区*/
    private ByteBuffer sBuffer = ByteBuffer.allocate(8354);
	
	/**
	 * 协议转换工具
	 */
	IotpBodyUtils ibu = new IotpBodyUtils();
	
	/* 测试用，用完删掉 */
	int index = 0;
	
	/************************************************      单例模式     ************************************************/
	
    /**
     * 创建一个新的实例 SingleSocketClientThread.
     */
    private SingleSocketWriteThread() {
    	new Thread(this).start();
    }
    
    /**
     * 私有对象变量
     */
    private static final SingleSocketWriteThread single = new SingleSocketWriteThread();
    
    /**
     * @Title: getInstance
     * @Description: 饿汉模式--单例（静态工厂方法） 
     * @param @return    参数
     * @return SingleSocketClientThread    返回类型
     * @throws
     */
    public static SingleSocketWriteThread getInstance() {
        return single;
    }
	
	/************************************************      单例模式     ************************************************/
	
	@Override
	public void run() {
        LOGGER.info("我启动了一个写信息的线程");
        // 轮询访问selector
        while (!SocketClientUtils.SOCKETBEAN_MAP.isEmpty()) {
        	try {
				Thread.sleep(15);
			} catch (InterruptedException e1) {
				e1.printStackTrace();
			}
        	for(SocketBean socketBean :SocketClientUtils.SOCKETBEAN_MAP.values()){
        		try {
        			if(socketBean.getConnectstatus() == 3 ){
        				try {
							if(socketBean.getSocketChannel() != null){
								send(socketBean);
							}
						} catch (CancelledKeyException e) {
							e.printStackTrace();
							continue;
						}
        			}
				} catch (Exception e) {
					e.printStackTrace();
					continue;
				}
        	}
        }
	}
	
	/**
	 * @Title: send
	 * @Description: 向服务端写数据
	 * @param @param json
	 * @param @param channel    参数
	 * @return void    返回类型
	 * @throws
	 */
	private void send(SocketBean socket){
		List<byte[]> sendmsg = null;
		try {
			sendmsg = SocketClientUtils.CHANNEL_MESSAGE_MAP.get(socket);
			if(sendmsg != null && sendmsg.size() > 0){
				byte[] msg = sendmsg.get(0);
				sendMsg(socket,msg);
				SocketClientUtils.CHANNEL_MESSAGE_MAP.get(socket).remove(0);
			}
		} catch (IOException e) {
			LOGGER.error(">>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>>");
			LOGGER.error("在向 "+socket.getServerip()+":"+socket.getServerport()+" 发送如下信息时抛出IO异常：");
			for(byte[] error:sendmsg){
				System.exit(0);
				LOGGER.error("          【 "+Arrays.toString(error)+" 】");
			}
			LOGGER.error(">>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>>");
			// 重新连接socket
			relink(socket);
			
		} catch (Exception e) {
			LOGGER.error(">>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>>");
			LOGGER.error("在向 "+socket.getServerip()+":"+socket.getServerport()+" 发送如下信息时出错：");
			for(byte[] error:sendmsg){
				LOGGER.error("          【 "+Arrays.toString(error)+" 】");
			}
			LOGGER.error(">>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>> >>>>");
		}
	}
	
	/**
	 * @Title: send_single_socket
	 * @Description: 向单个服务端写数据
	 * @param @param msglist  消息字节数组集合
	 * @param @param socket  SocketUtils.SOCKETBEAN_MAP.values()中存在SocketBean
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_single_socket(SocketBean socket,List<byte[]> msglist){
		boolean b = false;
		for(SocketBean socketBean :SocketClientUtils.SOCKETBEAN_MAP.values()){
			if(socketBean == socket){
				b = true;
			}
		}
		if(b){
			List<byte[]> list = SocketClientUtils.CHANNEL_MESSAGE_MAP.get(socket) == null ? (new ArrayList<>()) : SocketClientUtils.CHANNEL_MESSAGE_MAP.get(socket);
			list.addAll(msglist);
			SocketClientUtils.CHANNEL_MESSAGE_MAP.put(socket, list);
		}
		return b;
	}
	
	/**
	 * @Title: send_multi_socket
	 * @Description: 向多个服务端写数据
	 * @param @param msg  消息字节数组集合
	 * @param @param list_socket  SocketUtils.SOCKETBEAN_MAP.values()中存在SocketBean的集合，如果有不存在的则返回false
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_multi_socket(List<SocketBean> list_socket,List<byte[]> msg){
		Set<SocketBean> set_all = new HashSet<SocketBean>(SocketClientUtils.SOCKETBEAN_MAP.values());
		Set<SocketBean> set_list = new HashSet<SocketBean>(list_socket);
		if(set_list.size() > set_all.size() || set_list.isEmpty()){
			return false;
		}else{
			if(!set_all.containsAll(list_socket)){
				return false;
			}
		}
		for(SocketBean socketBean :set_list){
			List<byte[]> list = SocketClientUtils.CHANNEL_MESSAGE_MAP.get(socketBean) == null ? (new ArrayList<>()) : SocketClientUtils.CHANNEL_MESSAGE_MAP.get(socketBean);
			list.addAll(msg);
			SocketClientUtils.CHANNEL_MESSAGE_MAP.put(socketBean, list);
		}
		return true;
	}
	
	/**
	 * 重新连接
	 * @param socket
	 */
	private void relink(SocketBean socket){
		LOGGER.info("====== ====== ====== 连接 "+socket.getServerip()+":"+socket.getServerport() + " 正在重连 ====== ====== ====== ");
		try {
			socket.getSocketChannel().close();
		} catch (IOException e) {
			try {
				socket.getSocketChannel().close();
			} catch (IOException e1) {
				LOGGER.info("====== ====== ====== 连接 "+socket.getServerip()+":"+socket.getServerport() + " 关闭异常 ====== ====== ====== ");
			}
		}
		// 注销key
		socket.getSelectionKey().cancel();
		// 设置尝试连接状态
		socket.setConnectstatus(2);
		// 连接重新初始化
		socket.initialize();
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月20日
	 * @Title: send_single
	 * @Description: 向单个SocketBean发送协议
	 * @param @param socket
	 * @param @param iotp
	 * @param @return    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_single(SocketBean socket,IOTPBean iotp){
		return send_single_socket(socket,ibu.toByteArray(iotp));
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月20日
	 * @Title: send_multi
	 * @Description: 向多个SocketBean发送协议
	 * @param @param list_socket
	 * @param @param iotp
	 * @param @return    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_multi(List<SocketBean> list_socket,IOTPBean iotp){
		return send_multi_socket(list_socket, ibu.toByteArray(iotp));
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月27日
	 * @Title: send_single_address
	 * @Description: 向地址为address的服务端写数据
	 * @param @param address
	 * @param @param list_byte
	 * @param @return
	 * @param @throws InterruptedException    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_single_address(String address,List<byte[]> list_byte) throws InterruptedException{
		return SocketClientUtils.SOCKETBEAN_MAP.get(address) == null ? false : send_single_socket(SocketClientUtils.SOCKETBEAN_MAP.get(address), list_byte);
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月27日
	 * @Title: send_multi_address
	 * @Description: 向List<String>的多个地址服务端写数据
	 * @param @param addressList
	 * @param @param list_byte
	 * @param @return
	 * @param @throws InterruptedException    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_multi_address(List<String> addressList,List<byte[]> list_byte) throws InterruptedException{
		List<SocketBean> list_socket = new ArrayList<>();
		for (String address : addressList) {
			SocketBean sb = SocketClientUtils.SOCKETBEAN_MAP.get(address);
			if(sb == null){
				list_socket = null; // 清空对象，加速GC回收速度
				return false;
			}else{
				list_socket.add(sb);
			}
		}
		return send_multi_socket(list_socket, list_byte);
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月27日
	 * @Title: send_single_address
	 * @Description: 向地址为address的服务端写数据
	 * @param @param address
	 * @param @param iotp
	 * @param @return
	 * @param @throws InterruptedException    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_single_address(String address,IOTPBean iotp) throws InterruptedException{
		return SocketClientUtils.SOCKETBEAN_MAP.get(address) == null ? false : send_single_socket(SocketClientUtils.SOCKETBEAN_MAP.get(address), ibu.toByteArray(iotp));
	}
	
	/**
	 * @author 周西栋
	 * @date 2018年6月27日
	 * @Title: send_multi_address
	 * @Description: 向List<String>的多个地址服务端写数据
	 * @param @param addressList
	 * @param @param iotp
	 * @param @return
	 * @param @throws InterruptedException    参数
	 * @return boolean    返回类型
	 * @throws
	 */
	public boolean send_multi_address(List<String> addressList,IOTPBean iotp) throws InterruptedException{
		List<SocketBean> list_socket = new ArrayList<>();
		for (String address : addressList) {
			SocketBean sb = SocketClientUtils.SOCKETBEAN_MAP.get(address);
			if(sb == null){
				list_socket = null; // 清空对象，加速GC回收速度
				return false;
			}else{
				list_socket.add(sb);
			}
		}
		return send_multi_socket(list_socket, ibu.toByteArray(iotp));
	}
	
	/**
	 * @throws InterruptedException 
	 * @throws IOException 
	 * @author 周西栋
	 * @date 2018年6月26日
	 * @Title: sendMsg
	 * @Description: 发送字节数组
	 * @param @param socket
	 * @param @param msg    参数
	 * @return void    返回类型
	 * @throws
	 */
	private void sendMsg(SocketBean socket,byte[] msg) throws IOException, InterruptedException{
		int p = 0;
		while(p < msg.length){
			int sendSize = socket.getSocketChannel().write(ByteBuffer.wrap(msg,p,msg.length-p));
			p += sendSize;
			if(p != msg.length && sendSize > 0){
				System.out.println("我没有发完，只发了 "+ p +" 个");
				Thread.sleep(1);
			}else if(sendSize > 0){
				System.out.println("我发完了 "+ p +" 个");
				index += p;
				System.out.println("我累计发送了 "+ index +" 个");
			}
		}
	}
	
	/**
	 * 发送配置信息
	 * 
	 * @author lfy.xys
	 * @date 2018年6月27日
	 *
	 * @param ip
	 * @param port
	 * @param iotpBean
	 */
	public static void sendMsg(String ip, int port, IOTPBean iotpBean) {
		try {
			//创建连接
			SocketBean bean =  SocketClientUtils.SOCKETBEAN_MAP.get(ip + ":" + port);
			if(bean == null) {
				//如果不存在，则创建
				bean = new SocketBean(ip, port);
			}
			//获取已经连接的 socket，发送数据
			SingleSocketWriteThread.getInstance().send_single(bean, iotpBean);
			
		} catch (IOException e) {
			e.printStackTrace();
		}  
	}
}